home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / C / Applications / Eudora 1.3.1 / source / tcp.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-03-16  |  17.1 KB  |  610 lines  |  [TEXT/MPS ]

  1. #define FILE_NUM 37
  2. /* Copyright (c) 1990-1992 by the University of Illinois Board of Trustees */
  3. /************************************************************************
  4.  * functions for i/o over a pseudo-telnet MacTcp stream
  5.  * these functions are oriented toward CONVENIENCE, not performance
  6.  ************************************************************************/
  7. #pragma load EUDORA_LOAD
  8. #pragma segment TcpTrans
  9. static int TcpErr=0;
  10. static int CharsAvail=0;
  11. static UPtr TcpBuffer=nil;
  12. static long TcpStream=nil;
  13. static Boolean BeSilent;
  14. #ifndef SLOW_CLOSE
  15. pascal void FastCloseComplete();
  16. void TcpFastClose(void);
  17. #endif
  18.  
  19. /************************************************************************
  20.  * private functions
  21.  ************************************************************************/
  22. int GetHostByName(UPtr name, struct hostInfo *hostInfoPtr);
  23. int VerifyOpen(void);
  24. pascal void BindDone(struct hostInfo *hostInfoPtr, char *userData);
  25. pascal void TcpASR(StreamPtr tcpStream, unsigned short eventCode,
  26. UPtr userDataPtr, unsigned short terminReason, struct ICMPReport *icmpMsg);
  27. short WaitForChars(long timeout);
  28. #define TcpTrouble(which,err) TT(which,err,FILE_NUM,__LINE__)
  29. int TT(int which, int theErr, int file, int line);
  30.  
  31. /************************************************************************
  32.  * TCPConnectTrans - connect to the remote host.    This version uses MacTCP.
  33.  ************************************************************************/
  34. int TCPConnectTrans(UPtr serverName, short port,Boolean silently)
  35. {
  36.     Str255 scratch;
  37.     long bSize;
  38.     struct hostInfo hi;
  39.  
  40. #ifdef DEBUG
  41.     if (BUG12) port += 10000;
  42. #endif
  43.     BeSilent = silently;
  44.     Progress(NoBar,GetRString(scratch,WHO_AM_I));
  45.     if (TcpErr=VerifyOpen()) return (TcpErr);
  46.  
  47.     /*
  48.      * find the host
  49.      */
  50.     PCat(GetRString(scratch,DNR_LOOKUP),serverName);
  51.     Progress(NoBar,scratch);
  52.     if (TcpErr=GetHostByName(serverName,&hi))
  53.     {
  54.         TcpTrouble(BIND_ERR,TcpErr);
  55.         return(TcpErr);
  56.     }
  57.     
  58.     /*
  59.      * allocate a buffer for tcp, and create the stream
  60.      */
  61.     Progress(NoChange,GetRString(scratch,HOUSEKEEPING));
  62.     bSize = GetRLong(RCV_BUFFER_SIZE);
  63.     if ((RcvBuffer=NuHandle(bSize))==nil)
  64.     {
  65.         TcpErr=MemError();
  66.         WarnUser(MEM_ERR,TcpErr);
  67.         return(TcpErr);
  68.     }
  69.     bSize = GetRLong(TCP_BUFFER_SIZE);
  70. #ifdef DEBUG
  71.     if (BUG13) bSize *= 4;
  72.     else if (BUG2) bSize *= 2;
  73. #endif
  74.     if ((TcpBuffer=NuPtr(bSize))==nil)
  75.     {
  76.         TcpErr=MemError();
  77.         WarnUser(MEM_ERR,TcpErr);
  78.         return(TcpErr);
  79.     }
  80.     if (TcpErr=MyTcpCreate(&TcpStream,TcpBuffer,bSize,TcpASR))
  81.     {
  82.         TcpTrouble(TCP_TROUBLE,TcpErr);
  83.         return(TcpErr);
  84.     }
  85.     
  86.     /*
  87.      * now, try to open the connection
  88.      */
  89.     ComposeRString(scratch,CNXN_OPENING,serverName,*(unsigned long *)hi.addr);
  90.     Progress(NoChange,scratch);
  91.     TcpErr=MyTcpOpen(TcpStream,*hi.addr,port,1,GetRLong(OPEN_TIMEOUT));
  92.     if (TcpErr)
  93.         TcpTrouble(NO_SMTP_SERVER,TcpErr);
  94.     RcvSpot = -1;
  95.     
  96.     return(TcpErr);
  97. }
  98.  
  99. /************************************************************************
  100.  * TCPSendTrans - send some text to the remote host.    This version uses MacTCP.
  101.  ************************************************************************/
  102. int TCPSendTrans(short count, UPtr text,long size, ...)
  103. {
  104.     va_list extra_buffers;
  105.     if (!TcpStream) return(1);
  106.     if (size==0) return(noErr);     /* allow vacuous sends */
  107.     CycleBalls();
  108.     va_start(extra_buffers,size);
  109.     if (TcpErr=MyTcpSend(TcpStream,count,text,size,extra_buffers))
  110.         TcpTrouble(TCP_TROUBLE,TcpErr);
  111.     va_end(extra_buffers);
  112.     return(TcpErr);
  113. }
  114.  
  115. /************************************************************************
  116.  * TCPSendWDS - send a lot of text to the remote host.    This version uses MacTCP.
  117.  ************************************************************************/
  118. int TCPSendWDS(wdsEntry *theWDS)
  119. {
  120.     CycleBalls();
  121.     if (!TcpStream) return(1);
  122.     if (TcpErr=MyTcpSendWDS(TcpStream,theWDS))
  123.         TcpTrouble(TCP_TROUBLE,TcpErr);
  124.     return(TcpErr);
  125. }
  126.  
  127. /************************************************************************
  128.  * TCPRecvTrans - get some text from the remote host.  This version uses MacTCP.
  129.  ************************************************************************/
  130. int TCPRecvTrans(UPtr line,long *size)
  131. {
  132.     Str31 scratch;
  133.     
  134.     if (!TcpStream) return(1);
  135.     do
  136.     {
  137.         TcpErr = WaitForChars(GetRLong(RECV_TIMEOUT));
  138.         if (!TcpErr || TcpErr == connectionDoesntExist)
  139.             TcpErr=MyTcpRecv(TcpStream,line,size,0);
  140.     }
  141.     while (TcpErr==commandTimeout &&
  142.                  AlertStr(TIMEOUT_ALRT,Caution,GetRString(scratch,RECV_TIMEOUT))==1);
  143.  
  144.     if (TcpErr)
  145.     {
  146.         if (TcpErr!=commandTimeout && TcpErr!=userCancelled)
  147.             TcpTrouble(TCP_TROUBLE,TcpErr);
  148.     }
  149.     return(TcpErr);
  150. }
  151.  
  152. /************************************************************************
  153.  * TCPDisTrans - disconnect from the remote host.  This version uses MacTCP.
  154.  * You MUST wait for other side's close under MacOS; if you get a packet
  155.  * for a released, stream, the Mac will crash.    This should be avoided
  156.  * under A/UX, however, since it's unnecessary and takes so long.
  157.  ************************************************************************/
  158. int TCPDisTrans(void)
  159. {
  160.     Str63 buffer;
  161. #ifdef OLDCLOSE
  162.     int length;
  163. #endif
  164.  
  165. #ifndef SLOW_CLOSE
  166.     TcpFastClose();
  167. #endif
  168.     if (TcpStream)
  169.     {
  170.         Progress(NoBar,GetRString(buffer,CTB_CLOSING));
  171.         if (!TcpErr && !(TcpErr=MyTcpClose(TcpStream)))
  172.         {
  173. #ifdef OLDCLOSE
  174.           if (!IsAUX())
  175.             {
  176.                 for (length=sizeof(buffer);!TcpErr;length=sizeof(buffer))
  177.                 {
  178.                     if (!(TcpErr=WaitForChars(GetRLong(RECV_TIMEOUT))))
  179.                             TcpErr=MyTcpRecv(TcpStream,buffer,&length,0);
  180.                 }
  181.             }
  182. #endif
  183.         }
  184.     }
  185.     if (TcpErr==connectionDoesntExist)
  186.         TcpErr = noErr;
  187.     return(TcpErr);
  188. }
  189.  
  190. /************************************************************************
  191.  * TCPDestroyTrans - Be sure the connection is destroyed
  192.  ************************************************************************/
  193. int TCPDestroyTrans(void)
  194. {
  195.     if (TcpStream)
  196.     {
  197.         TCPiopb pb;
  198.         if (!MyTcpStatus(TcpStream,&pb) && pb.csParam.status.connectionState)
  199.         {
  200.             MyTcpAbort(TcpStream);
  201.             Pause(60);                /* believe me, this is the safe thing to do */
  202.         }
  203.         MyTcpRelease(TcpStream);
  204.         TcpStream = nil;
  205.     }
  206.     if (TcpBuffer)
  207.     {
  208.         DisposPtr(TcpBuffer);
  209.         TcpBuffer = nil;
  210.     }
  211.     if (RcvBuffer) ZapHandle(RcvBuffer);
  212.     return(TcpErr);
  213. }
  214.  
  215.  
  216. /************************************************************************
  217.  * VerifyOpen - make sure the MacTCP driver is going
  218.  ************************************************************************/
  219. int VerifyOpen(void)
  220. {
  221.     static short refN = 0;
  222.     WhyTCPTerminated = 0;
  223.     if (!refN)
  224.     {
  225.         Str255 driverName;
  226.         GetRString(driverName,TCP_DRIVER);
  227.         if (TcpErr = OpenDriver(driverName,&refN))
  228.         {
  229.             TcpTrouble(TCP_TROUBLE,TcpErr);
  230.             return(TcpErr);
  231.         }
  232.         MyTcpRefN(refN);
  233.     }
  234.     return(noErr);
  235. }
  236.  
  237. /************************************************************************
  238.  * TCPTransError - report our most recent error
  239.  ************************************************************************/
  240. int TCPTransError(void)
  241. {
  242.     return(TcpErr);
  243. }
  244.  
  245. #pragma segment Main
  246. /************************************************************************
  247.  * BindDone - report that the resolver has done its duty
  248.  ************************************************************************/
  249. pascal void BindDone(struct hostInfo *hostInfoPtr, char *userData)
  250. {
  251. #pragma unused(hostInfoPtr)
  252.     if (userData) *(int *)userData = 1;
  253.     return;
  254. }
  255. #pragma segment TcpTrans
  256.  
  257. /************************************************************************
  258.  * WaitForChars - spin, giving everybody else time, until chars available
  259.  ************************************************************************/
  260. short WaitForChars(long timeout)
  261. {
  262.     EventRecord event;
  263.     long ticks=TickCount();
  264.     static long waitTicks=0;
  265.     long tookTicks;
  266.     Boolean result;
  267.     long timeoutTicks = ticks + 60*timeout;
  268.     TCPiopb pb;
  269.     short err;
  270.  
  271.     if (!InBG) waitTicks = 0;
  272.     do
  273.     {
  274.         if (TickCount()-ticks  > 10)
  275.         {
  276.             CyclePendulum();
  277.             ticks=TickCount();
  278.             if (ticks >timeoutTicks) return(commandTimeout);
  279.         }
  280.         tookTicks = TickCount();
  281.         result = WNE(everyEvent,&event,waitTicks);
  282.         tookTicks = TickCount()-tookTicks;
  283.         if (InBG)
  284.             if (tookTicks > waitTicks+1)
  285.                 waitTicks = MIN(120,tookTicks);
  286.             else
  287.                 waitTicks = MAX(0,waitTicks>>1);
  288.         if (result)
  289.         {
  290.             (void) MiniMainLoop(&event);
  291.             if (CommandPeriod) return(userCancelled);
  292.         }
  293.         ClearICMP();
  294.         if (err=MyTcpStatus(TcpStream,&pb)) return(err);
  295.         if (pb.csParam.status.connectionState != 8) return(connectionDoesntExist);
  296.     }
  297.     while (!pb.csParam.status.amtUnreadData);
  298.     CharsAvail = 0;
  299.     return(0);
  300. }
  301.  
  302. /************************************************************************
  303.  * GetHostByName - get host information, given a hostname
  304.  * this routine maintains a small, unflushable cache.
  305.  ************************************************************************/
  306. int GetHostByName(UPtr name, struct hostInfo *hostInfoPtr)
  307. {
  308.     Str255 cName;
  309.     static struct hostInfo cache[4];
  310.     static short nextInfo;
  311.     short info;
  312.     HostInfoQHandle hiqh;
  313. #define HI (*hiqh)->hi
  314.     
  315.     strncpy(cName,name+1,*name);
  316.     cName[*name] = 0;
  317.     for (info=0;info<sizeof(cache)/sizeof(struct hostInfo);info++)
  318.         if (!strcmp(cName,cache[info].cname))
  319.         {
  320.             *hostInfoPtr = cache[info];
  321.             return (noErr);
  322.         }
  323.     if ((hiqh=NewZH(HostInfoQ))==nil) return(MemError());
  324.     LL_Push(HIQ,hiqh);
  325.     MoveHHi(hiqh);
  326.     HLock(hiqh);
  327.     if (TcpErr = OpenResolver(nil))
  328.     {
  329.         TcpTrouble(BIND_ERR,TcpErr);
  330.         return(TcpErr);
  331.     }
  332.     strncpy(cName,name+1,*name);
  333.     cName[*name] = 0;
  334.     HI.rtnCode = inProgress;
  335.     TcpErr = StrToAddr(cName,&HI,BindDone,nil);
  336.     if (TcpErr==cacheFault || !TcpErr) TcpErr=SpinOn(((short *)&HI.rtnCode)+1,0);
  337.     if (!CommandPeriod)
  338.     {
  339.         if (!TcpErr)
  340.         {
  341.             cache[nextInfo] = *hostInfoPtr = HI;
  342.             strcpy(cache[nextInfo].cname,cName);    /* remember the name we're given,
  343.                                                                                              not the real cname */
  344.             nextInfo = (nextInfo + 1) % (sizeof(cache)/sizeof(struct hostInfo));
  345.         }
  346.         LL_Remove(HIQ,hiqh,(HostInfoQHandle));
  347.         if (!HIQ) CloseResolver();
  348.         DisposHandle(hiqh);
  349.     }
  350.     return(TcpErr);
  351. }
  352.  
  353. /************************************************************************
  354.  * GetHostByAddr - get host information, given an address
  355.  * this routine maintains a small, unflushable cache.
  356.  ************************************************************************/
  357. int GetHostByAddr(struct hostInfo *hostInfoPtr,long addr)
  358. {
  359.     static struct hostInfo lastInfo;
  360.     static long lastAddr;
  361.     HostInfoQHandle hiqh;
  362. #define HI (*hiqh)->hi
  363.     
  364.     if (addr==lastAddr)
  365.     {
  366.         *hostInfoPtr = lastInfo;
  367.         return(noErr);
  368.     }
  369.  
  370.     if ((hiqh=NewZH(HostInfoQ))==nil) return(MemError());
  371.     LL_Push(HIQ,hiqh);
  372.     MoveHHi(hiqh);
  373.     HLock(hiqh);
  374.     if (TcpErr = OpenResolver(nil))
  375.     {
  376.         TcpTrouble(BIND_ERR,TcpErr);
  377.         return(TcpErr);
  378.     }
  379.  
  380.     HI.rtnCode = inProgress;
  381.     TcpErr = AddrToName(addr,&HI,BindDone,nil);
  382.     if (TcpErr==cacheFault || !TcpErr) TcpErr=SpinOn(((short *)&HI.rtnCode)+1,0);
  383.     if (!CommandPeriod)
  384.     {
  385.         LL_Remove(HIQ,hiqh,(HostInfoQHandle));
  386.         if (!HIQ) CloseResolver(); else ASSERT(0);
  387.         if (!TcpErr)
  388.         {
  389.             short l = strlen(HI.cname);
  390.             if (HI.cname[l-1]=='.') HI.cname[l-1]=0;    /* for benefit of broken sendmails */
  391.             lastInfo = *hostInfoPtr = HI;
  392.             lastAddr = addr;
  393.         }
  394.         DisposHandle(hiqh);
  395.     }
  396.  
  397.     return(TcpErr);
  398. }
  399.  
  400. /************************************************************************
  401.  * TcpTrouble - report an error with TCP and break the connection
  402.  ************************************************************************/
  403. int TT(int which, int theErr, int file, int line)
  404. {
  405.     if (!BeSilent && !CommandPeriod)
  406.     {
  407.         Str255 message;
  408.         Str255 tcpMessage;
  409.         Str63 debugStr;
  410.         Str31 rawNumber;
  411.         
  412.         NumToString(theErr,rawNumber);
  413.         GetRString(message, which);
  414.         if (-23000>=theErr && theErr >=-23048)
  415.             GetRString(tcpMessage,MACTCP_ERR_STRN-22999-theErr);
  416.         else if (2<=theErr && theErr<=9)
  417.             GetRString(tcpMessage,MACTCP_ERR_STRN+theErr+(23048-23000));
  418.         else
  419.             *tcpMessage = 0;
  420.  
  421.         ComposeRString(debugStr,FILE_LINE_FMT,file,line);
  422.         MyParamText(message,rawNumber,tcpMessage,debugStr);
  423.         (void) ReallyDoAnAlert(BIG_OK_ALRT,Caution);
  424.     }
  425.     return(TcpErr = theErr);
  426. }
  427.  
  428. /************************************************************************
  429.  * TCPSilenceTrans - turn off error reports from tcp routines
  430.  ************************************************************************/
  431. void TCPSilenceTrans(Boolean silence)
  432. {
  433.     BeSilent = silence;
  434. }
  435.  
  436. /************************************************************************
  437.  * ClearICMP - warn about an ICMP error
  438.  ************************************************************************/
  439. short ClearICMP(void)
  440. {
  441.     if (ICMPAvail && GetRLong(ICMP_SECONDS))
  442.     {
  443.         Str255 scratch;
  444.         Boolean oldAlertsTimeout = AlertsTimeout;
  445.         short report = ICMPMessage.reportType;
  446.  
  447.         if (report<0 || report>8) report = 9;
  448.         AlertsTimeout = True;
  449.         GetRString(scratch,ICMP_STRN+report+1);
  450.         AlertTicks = GetRLong(ICMP_SECONDS)*60 + TickCount();
  451.         InBG = 0; /* Don't ask, please */
  452.         AlertStr(ICMP_ALRT,Caution,scratch);
  453.         AlertTicks = 0;
  454.         AlertsTimeout = oldAlertsTimeout;
  455.         ICMPAvail = 0;                                            /* resetting the flag here
  456.                                                                                      means we may lose ICMP messages
  457.                                                                                      while the alert is up.  That
  458.                                                                                      is GOOD. */
  459.         return(ICMPMessage.reportType+1);
  460.     }
  461.     return(0);
  462. }
  463.  
  464. /************************************************************************
  465.  * TCPWhoAmI - return the mac's tcp name
  466.  ************************************************************************/
  467. UPtr TCPWhoAmI(Uptr who)
  468. {
  469.     uLong addr, mask;
  470.     MyHostid((ip_addr *)&addr,&mask);
  471.     return(ComposeRString(who,TCP_ME,addr));
  472. }
  473.  
  474. /************************************************************************
  475.  * GetTCPStatus - return connection info
  476.  ************************************************************************/
  477. int GetTCPStatus(TCPiopb *pb)
  478. {
  479.     return(MyTcpStatus(TcpStream,pb));
  480. }
  481.  
  482. #ifndef SLOW_CLOSE
  483. /************************************************************************
  484.  * TcpFastClose - close the current stream, but don't wait for it
  485.  *
  486.  * We allocate a parameter block on the heap, and stuff the current stream
  487.  * info into it.  Then, we start a receive.  The ioCompletion routine for the
  488.  * receive will start another one, until there is nothing more to receive.
  489.  * then the ioCompletion routine starts a close.  When the close completes
  490.  * or if there is an error anywhere along the way, the csCode of the block
  491.  * is set to TCPRelease, which signals TcpFastFlush (called from the main
  492.  * loop and the Cleanup routine) to release the stream (and the pb).
  493.  *
  494.  * if we can't allocate the pb, we just return, and let the stream go through
  495.  * the normal close process
  496.  ************************************************************************/
  497. void TcpFastClose(void)
  498. {
  499.     FastPBPtr mypb = NewPtrClear(sizeof(FastPB));
  500.     if (mypb)
  501.     {
  502.         /*
  503.          * link it onto the list
  504.          */
  505.         mypb->next = FastList;
  506.         FastList = mypb;
  507.         
  508.         /*
  509.          * fill in invariant values
  510.          */
  511.         mypb->pb.ioCRefNum = MyTcpRefN(0);
  512.         mypb->pb.tcpStream = TcpStream;
  513.         mypb->pb.ioCompletion = FastCloseComplete;
  514.         mypb->streamBuffer = TcpBuffer;
  515.         
  516.         /*
  517.          * start a receive
  518.          */
  519.         mypb->pb.csCode = TCPRcv;
  520.         mypb->pb.csParam.receive.commandTimeoutValue = 60;    /* let it go for a minute */
  521.         mypb->pb.csParam.receive.rcvBuff = mypb->buffer;
  522.         mypb->pb.csParam.receive.rcvBuffLen = sizeof(mypb->buffer);
  523.         if (PBControl(mypb,True)) mypb->pb.csCode = TCPRelease;    /* kill it on error */
  524.         
  525.         /*
  526.          * don't have to worry about the stream anymore
  527.          */
  528.         TcpStream = nil;
  529.         TcpBuffer = nil;
  530.     }
  531. }
  532.  
  533. #pragma segment Main
  534.  
  535. /************************************************************************
  536.  * TcpFastFlush - run through the queue, killing off defunct streams
  537.  ************************************************************************/
  538. void TcpFastFlush(Boolean destroy)
  539. {
  540.     FastPBPtr this,next,last=nil;
  541.     
  542.     for (this=FastList;this;this=next)
  543.     {
  544.         next = this->next;
  545.         if (destroy || this->pb.csCode==TCPRelease)
  546.         {
  547.             /*
  548.              * cut it out of the herd
  549.              */
  550.             if (FastList==this) FastList=next;
  551.             if (last) last->next = next;
  552.             
  553.             /*
  554.              * and finish it off
  555.              */
  556.             MyTcpRelease(this->pb.tcpStream);
  557.             DisposPtr(this->streamBuffer);
  558.             DisposPtr(this);
  559.         }
  560.         else
  561.             last = this;
  562.     }
  563. }
  564.  
  565. /************************************************************************
  566.  * FastCloseComplete - ioCompletion routine for fast close
  567.  ************************************************************************/
  568. FastPBPtr        *GetParmBlockPtr()        = { 0x2008 };     // move.l a0, d0 ;move the pointer to where MPW C places function results
  569. pascal void FastCloseComplete()
  570. {
  571.     FastPBPtr pb = GetParmBlockPtr();
  572.     
  573.     if (pb->pb.csCode==TCPClose)
  574.     {
  575.         pb->pb.csCode = TCPRelease;                    /* tell TcpFastFlush to kill it */
  576.         return;
  577.     }
  578.     else if (pb->pb.ioResult==noErr)    /* still going */
  579.         pb->pb.csParam.receive.rcvBuffLen = sizeof(pb->buffer);
  580.     else  /* receive failed */
  581.     {
  582.         pb->pb.csCode = TCPClose;
  583.         pb->pb.csParam.close.validityFlags = 0;
  584.     }
  585.     if (PBControl(pb,True)) pb->pb.csCode = TCPRelease;    /* kill it on error */
  586. }
  587. #endif
  588.  
  589. /************************************************************************
  590.  * TcpASR - asynchronous notification routine
  591.  ************************************************************************/
  592. pascal void TcpASR(StreamPtr tcpStream, unsigned short eventCode,
  593.     UPtr userDataPtr, unsigned short terminReason, struct ICMPReport *icmpMsg)
  594. {
  595. #pragma unused(userDataPtr)
  596.     if (tcpStream==TcpStream)
  597.     {
  598.         if (eventCode==TCPDataArrival) CharsAvail = 1;
  599.         else if (eventCode==TCPICMPReceived)
  600.         {
  601.             ICMPAvail = 1;
  602.             ICMPMessage = *icmpMsg;
  603.         }
  604.         else if (eventCode==TCPTerminate)
  605.         {
  606.             WhyTCPTerminated = terminReason;
  607.         }
  608.     }
  609. }
  610.